#include <cstdio>
#include <iostream>
#include "kbest.h"
#include "papi.h"

/* min working set size 1KB*/
#define MINBYTES ( 1 << 10)
/* max working set size 16M*/
#define MAXBYTES (1 << 30)
/*stride range from 1 to 128*/
#define MAXSTRIDE 64
#define MAXELEMS (MAXBYTES / sizeof(int))

int data[MAXELEMS];

using namespace std;

static void test(int elems, int stride) {
	int i, result = 0;
	volatile int sink;

	for (i = 0; i < elems; i += stride) {
		result += data[i];
	}
	sink = result;
}

double run(int size, int stride) {
	int retval = 0;
	long_long start, end;
	int elems = size / sizeof(int);

	test(elems, stride); //warm up the cache

	kbest<long_long, long_long> best;
	do {
		start = PAPI_get_real_nsec();
		test(elems, stride);
		end = PAPI_get_real_nsec();
		retval = best.add(end - start, end - start);
	} while (0 == retval);

	if (retval < 0) {
		cerr << "can NOT get invalid result." << endl;
	}

	return (size / stride) / ((long double) best.get_result() / 1000000000)
			/ (1024 * 1024);
}

void init_data(int data[], int elems) {
	for (int i = 0; i < elems; i++) {
		data[i] = 1;
	}
}

int main() {
	int size;
	int stride;

	init_data(data, MAXELEMS);

	if (PAPI_VER_CURRENT != PAPI_library_init(PAPI_VER_CURRENT)) {
		cerr << "can NOT initialize PAPI library." << endl;
		return -1;
	}

	cout << '\t';
	for (stride = 1; stride <= MAXSTRIDE; stride++) {
		cout << stride << '\t';
	}
	cout << endl;
	for (size = MAXBYTES; size >= MINBYTES; size >>= 1) {
		cout << size / 1024 << "K\t";
		for (stride = 1; stride <= MAXSTRIDE; stride++) {
			cout << (int)run(size, stride) << '\t';
		}
		cout << endl;
	}
}
